home *** CD-ROM | disk | FTP | other *** search
/ TeX 1995 July / TeX CD-ROM July 1995 (Disc 1)(Walnut Creek)(1995).ISO / tex-k / impatient / index2.icn < prev    next >
Text File  |  1990-08-09  |  7KB  |  239 lines

  1. # This Icon program is the second pass of the indexing process for TeX for the
  2. # Impatient.  It must be preceded by index1 and by a sort of the intermediate
  3. # file.
  4.  
  5. # This program was written by Paul Abrahams
  6.  
  7. record topic_entry(term, type, groupchar, pages, level)
  8. record pgrec(number, flags)
  9. record term_list_record(term_list, start)
  10. procedure main(a)
  11.     local gen, pages, term, topic
  12.     local groupchar
  13.  
  14.     write(&errout, "Second indexing pass has started.")
  15.  
  16. # Each pass through this loop produces the entry for a single topic
  17. # or subtopic, including both the text of the topic and its pages.
  18.  
  19.     every topic := get_topic_info() do {
  20.  
  21. # If we're starting a new group (initial character), produce the macro
  22. # for it.
  23.         if topic.level = 1 then { # only primary topics affect the group
  24.             if not(\groupchar == topic.groupchar) then
  25.                 write("\\indexgroup ", groupchar := topic.groupchar)
  26.             }
  27.         else
  28.             topic.type := "N" # subtopics are always printed normally
  29.  
  30. # Write the index term
  31.         writes("\\indexentry {", topic.level - 1, "}{",
  32.             edit_term(topic.term), "}{", topic.type, "}{")
  33.  
  34. # Write the list of pages
  35.         write(edit_pages(topic.pages, topic.term), "}")
  36.         }
  37. end
  38.  
  39. procedure get_topic_info()
  40.     local page, type, full_term, flags # info in an index item
  41.     local term      # the index term to be printed (part of full_term)
  42.     local item_text # holds an input item to be parsed
  43.     local topic     # the topic we're now working on
  44.     local term_list_info # returned term_list_record from get_term_list
  45.     local term_list # list of index terms extracted from the input item
  46.     local first         # position of first thing in term_list to print
  47.     local t             # loop variable
  48.     local term1         # first term in full_term, usually the only one
  49.  
  50.     term_list := []
  51.  
  52. # At the start of each pass through this loop, `topic' contains the text of
  53. # the index topic most recently seen together with the pages seen so far for
  54. # that index topic.
  55.  
  56.     every !&input ? (tab(find("@@@")\1), move(3), item_text := tab(0)) do {
  57.  
  58. # Dissect the original index item, discarding the key
  59.  
  60.         item_text ? (full_term := tab(find("::")), move(2),
  61.          type := tab(find("::")), move(2),
  62.          page := tab(many('-0123456789*')), flags := tab(0))
  63. # a page of * indicates a see-also
  64.  
  65.         term_list_info := get_term_list(full_term, term_list)
  66.         term_list := term_list_info.term_list
  67.         if type == (\topic).type then # no change of type
  68.             first := term_list_info.start
  69.         else
  70.             first := 1 # change of type, so all terms are different
  71.         term1 := term_list[\first]
  72.  
  73. # If we've finished the current topic, produce it and start the next one
  74.         
  75.         if \first then {
  76.             suspend \topic
  77.             topic := topic_entry(term1, type, find_groupchar(term1), [], first)
  78.             every t := !term_list[first + 1:0] do {
  79.                 suspend topic
  80.                 topic.term := t; topic.type := "N"; topic.level +:= 1
  81.             }    }
  82.         put(topic.pages,
  83.             if page == "*" then
  84.                 flags # flags here is the see-also
  85.             else
  86.                 pgrec(page, cset(flags)))
  87.         }
  88.     suspend topic
  89.     fail
  90. end
  91.  
  92. procedure edit_term(term)
  93. # This procedure edits `term' into a proper argument for \indexterm
  94.  
  95.     if term == " " then
  96.         term := "\\visiblespace"
  97.     else if *term = 1 then
  98.         term := "\\char `\\" || term
  99.     else if match("^^", term) then
  100.         term := "\\twocarets " || term[3:0]
  101.     else if term == "$$" then
  102.         term := "\\$\\$"
  103. # $$ is the only other 2-character sequence that has to be protected.
  104.     return term
  105. end    
  106.  
  107. procedure edit_pages(l, term)
  108. # edit_pages removes duplicate pages from the page list, produces the
  109. # macro call for a principal entry, and coalesces page ranges.
  110. # It also converts negative numbers to roman numerals.
  111. # Each element of l is a pgrec, except that the last (and possibly only)
  112. # element may be a see-also string starting with *.
  113. # The result is a list of strings
  114.     local pg, n, m, pf, see_also, pagelist
  115.     local l1, k
  116.  
  117. # If the last element of l is a string, remove it and set it aside.
  118. # It's a see-also.
  119.     if type(l[-1]) == "string" then
  120.         {see_also := l[-1]; l := l[1:-1]}
  121.  
  122.  
  123. # First pass through the page list, coalescing duplicates and combining
  124. # their flags.
  125.  
  126.     l1 := []
  127.     while *l > 0 do {
  128.         pg := pop(l); n := pg.number; pf := pg.flags
  129.  
  130. # Loop over pages 2..k within a group
  131.  
  132.         while n = l[1].number do
  133.             pf ++:= pop(l).flags
  134.  
  135.         if *(pf ** 'BE') = 2 then # delete B and E if they both occur
  136.             pf --:= 'BE'
  137.         put(l1, pgrec(n, pf))
  138.         }
  139.  
  140. # Now l1 has no duplicates and no trivial page ranges.  Replace each
  141. # page range by a single entry, inverting the order for negative page
  142. # numbers since those indicate roman numerals.
  143. # When we're done, l1 has a list of strings rather than a list of pgrecs.
  144.  
  145.     l := l1; l1 := []
  146.     while *l > 0 do {
  147.         pg := pop(l); n := pg.number; pf := pg.flags
  148.         if *(pf ** 'E') > 0 then {
  149.             every write(errfiles(), "Unmatched end of page range, page ",
  150.              integer(n), ", index term `", term, "'!")
  151.             pf --:= 'E'
  152.             }
  153.         if *(pf ** 'B') > 0 then { # beginning a page range
  154.             every k := 1 to *l do {
  155.                 pf ++:= l[k].flags
  156.                 if *(pf ** 'E') > 0 then break
  157.                 }
  158.             if *(pf ** 'E') = 0 then {
  159.                 every write(errfiles(), "Unmatched beginning of page range, page ",
  160.                  integer(n), ", index term `", term, "'!")
  161.                 pf := pg.flags
  162.                 }
  163.             else {
  164.                 m := l[k].number
  165.                 if m < 0 then { # roman numerals
  166.                     m := "\\r" || -m
  167.                     n := "\\r" || -n
  168.                     }
  169.                 n := string(n || "--" || m)
  170.                 l := l[k+ 1:0]
  171.             }    }
  172.         else if n < 0 then
  173.             n := "\\r" || -n
  174.         if *(pf ** 'P') > 0 then
  175.             n := "\\pp{" || n || "}"
  176.         put(l1, n)
  177.         }
  178.  
  179. # Now l1 is a list of page numbers and page ranges.
  180. # If it's empty and we have a see-also, make it a \see and return it.
  181.  
  182.     if *l1 = 0 then
  183.         return "\\see{" || \see_also || "}" | ""
  184.  
  185. # Turn l1 into a string and insert the comma commands \ic and \c
  186. # \ic goes at the beginning, \c between the remaining elements.
  187.  
  188.     pagelist := "\\ic " || pop(l1) | ""
  189.     every pagelist ||:= "\\c " || !l1
  190.  
  191. # Now attach the see-also to pagelist if we had one and return the result
  192.  
  193.     return pagelist || ("\\seealso{" || \see_also || "}" | "")
  194. end
  195.  
  196. procedure find_groupchar(t)
  197. # This procedure finds the character that heads the group containing
  198. # the index term `t'.  We want all special characters in a single group
  199. # and all digits in a single group.
  200. # A term that begins with `\<c' or `\c' or `.c' is grouped as `c'.
  201.     local c
  202.     static printable, specials
  203.     initial {
  204.         printable := &ascii[33:-1]
  205.         specials := string(printable -- (&ucase ++ &lcase ++ &digits))
  206.         }
  207.  
  208.     return map(
  209.             if t ? (tab(many('\\.<')), c := move(1)) then c
  210.             else if any(specials, t[1]) then "+"
  211.             else if any(&digits, t[1]) then "0"
  212.             else t[1] | "",
  213.         &lcase, &ucase)
  214. end
  215.  
  216. procedure get_term_list(ft, tl)
  217. # `ft' is the full term just read in, `tl' is the current term list
  218. # return a record containing all the terms and the position of the first
  219. # one that's different from the previous full term
  220.     local tl1, pos, pos1, first, k
  221.     
  222.     tl1 := []
  223.     pos := 1
  224.     every pos1 := (find("//", ft) | 0) do
  225.         {put(tl1, ft[pos:pos1]); pos := pos1 + 2}
  226.  
  227.     first := &null    
  228.     every k := 1 to *tl1 do
  229.         if not(tl1[k] == tl[k]) then
  230.             {first := k; break}
  231.     return term_list_record(tl1, first)
  232. end    
  233.  
  234. procedure errfiles()
  235.     static errf
  236.     initial
  237.         errf := open("index.err", "w")
  238.     suspend &errout | errf
  239. end